server.js ➔ ... ➔ ???   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 6.7968

Importance

Changes 9
Bugs 0 Features 0
Metric Value
cc 3
c 9
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 9.4285
ccs 1
cts 4
cp 0.25
crap 6.7968
1
/**
2
 * Server main
3
 *
4
 * @since 1.0.0
5
 */
6
7 4
require('dotenv').config();
8 4
require('babel-register');
9 4
require('babel-polyfill');
10
11
import Hapi from 'hapi';
12
13 4
const vision = require('vision');
14 4
const inert = require('inert');
15 4
const HapiAuthJwt2 = require('hapi-auth-jwt2');
16 4
const HapiReactViews = require('hapi-react-views');
17 4
const HapiErrorHandler = require('./middleware/error-handler');
18 4
const HapiTransformer = require('./middleware/transformer');
19 4
const HapiAuthChecker = require('./middleware/auth-info-checker');
20
21 4
const commonApiRouter = require('./router/common-api-router');
22 4
const statusApiRouter = require('./router/status-api-router');
23 4
const statusTypeApiRouter = require('./router/status-type-api-router');
24 4
const deviceTypeApiRouter = require('./router/device-type-api-router');
25 4
const baseRouter = require('./router/ui-router');
26 4
const User = require('./repository/User');
27 4
const DeviceType = require('./repository/DeviceType');
28 4
const StatusType = require('./repository/StatusType');
29
30 4
const config = require('./config/server.config');
31 4
const NotifierError = require('./common/Error');
32
33 4
const util = require('./common/common-util');
34 4
const logger = require('winston');
35
36 4
const server = new Hapi.Server();
37 4
server.connection({ port: process.env.PORT || config.defaults.port });
38
39 4
server.state('token', {
40
  ttl: config.auth.tokenTTL,
41
  isSecure: process.env.USE_HTTPS && process.env.USE_HTTPS === 'true',
42
  path: '/',
43
});
44
45 4
const plugins = [
46
  { register: vision },
47
  { register: inert },
48
  { register: HapiAuthJwt2 },
49
  { register: HapiErrorHandler, options: { apiPrefix: config.url.apiPrefix, errorView: 'Error' } },
50
  { register: HapiAuthChecker,
51
    options: {
52
      excludeUrlPatterns: [new RegExp(`^${config.url.apiPrefix}`), new RegExp('^/logout')],
53
    },
54
  },
55
  { register: HapiTransformer, options: { apiPrefix: config.url.apiPrefix } },
56
];
57
58 4
const _setAuthStrategy = () => {
59 4
  server.auth.strategy('jwt', 'jwt', {
60
    key: process.env.SECRET_KEY || config.auth.secretKey,
61
    validateFunc: (decoded, request, callback) => {
62
      // Check token IP address
63
      const clientIP = util.getClientIp(request);
64 2
      if (clientIP !== decoded.ip) {
65
        logger.warn(`[Auth] This client IP is matched with token info.: decoded.ip => ${decoded.ip}, client IP => ${clientIP}`);
66
        return callback(new NotifierError(NotifierError.Types.AUTH_TOKEN_INVALID), false);
67
      }
68
      // Check token expiration
69 2
      if (decoded.exp < new Date().getTime()) {
70
        logger.warn(`[Auth] This auth token is expired.: decoded.exp => ${decoded.exp}, now => ${new Date().getTime()}`);
71
        return callback(new NotifierError(NotifierError.Types.AUTH_TOKEN_EXPIRED), false);
72
      }
73
      return User.find({ username: decoded.username })
74
        .then((accounts) => {
75 4
          if (!accounts || accounts.length === 0) {
76
            logger.warn(`[Auth] This account is not exist.: ${decoded.username}`);
77
            return callback(new NotifierError(NotifierError.Types.AUTH_USER_NOT_EXIST, { username: decoded.username }), false);
78
          }
79
          return callback(null, true, accounts[0]);
80
        })
81
        .catch((e) => {
82
          logger.error(`[DB] DB error occurred: ${e.message}`);
83
          callback(new NotifierError(NotifierError.Types.DB), false);
84
        });
85
    },
86
    verifyOptions: { algorithms: ['HS256'] },
87
  });
88
89 4
  server.auth.default('jwt');
90
};
91
92 4
const _setViewEngine = () => {
93 4
  server.views({
94
    engines: { jsx: HapiReactViews, js: HapiReactViews },
95
    relativeTo: __dirname,
96
    path: config.directory.component,
97
    defaultExtension: 'js',
98
  });
99
};
100
101 4
const _setRoutes = (extraRoutes) => {
102
  // for static assets
103 4
  server.route(commonApiRouter);
104 4
  server.route(statusApiRouter);
105 4
  server.route(statusTypeApiRouter);
106 4
  server.route(deviceTypeApiRouter);
107 4
  server.route(baseRouter);
108 4
  if (extraRoutes) {
109
    server.route(extraRoutes);
110
  }
111
};
112
113 4
const _setInitalData = () => {
114 4
  let promises = [];
115 4
  return Promise.all([User.count(), DeviceType.count(), StatusType.count()])
116
    .then(([userCount, deviceTypeCount, statusTypeCount]) => {
117 4
      if (userCount === 0) {
118 1
        promises = promises.concat(config.initialData.users.map(user => User.add(user)));
119
      }
120 4
      if (deviceTypeCount === 0) {
121 5
        promises = promises.concat(config.initialData.deviceTypes.map(dt => DeviceType.add(dt)));
122
      }
123 4
      if (statusTypeCount === 0) {
124 2
        promises = promises.concat(config.initialData.statusTypes.map(user => StatusType.add(user)));
125
      }
126
    })
127 4
    .then(() => Promise.all(promises))
128 4
    .then(result => logger.log('[SET INITIAL DATA] success: ', result));
129
};
130
131 4
exports.addPlugin = (pluginSetting) => {
132
  plugins.push(pluginSetting);
133
};
134
135 4
exports.start = extraRoutes => server.register(plugins)
136
    .then(() => {
137 4
      _setAuthStrategy();
138 4
      _setViewEngine();
139 4
      _setRoutes(extraRoutes);
140
    })
141 4
    .then(() => _setInitalData())
142 4
    .then(() => server.start())
143
    .then(() => {
144 4
      logger.log('Server running at:', server.info.uri);
145 4
      return server;
146
    })
147
    .catch((error) => { throw error; });
148